Skip to content

fix: pass golangci-lint 2.11+ checks#307

Merged
alexei-led merged 1 commit intomasterfrom
fix/golangci-lint-2.11
Mar 13, 2026
Merged

fix: pass golangci-lint 2.11+ checks#307
alexei-led merged 1 commit intomasterfrom
fix/golangci-lint-2.11

Conversation

@alexei-led
Copy link
Owner

Summary

  • Fix gosec G118 (context cancel not called) in all 8 chaos command files by moving defer cancel() inside goroutines — each goroutine owns its context lifecycle
  • Fix pre-existing closure capture bug in iptables/loss.go where ctx was reassigned with = each iteration but captured by closure, causing all goroutines to share the last context value. Switched to per-iteration iptCtx with := (matching the netem pattern)
  • Remove stale //nolint:revive directive on pkg/util/util.go

Closes #306

Details

golangci-lint 2.11+ introduced stricter gosec G118 checking that flags context.WithTimeout/context.WithCancel when the cancel function isn't directly called or deferred. The old pattern collected cancels in a slice and deferred a cleanup loop after wg.Wait() — gosec couldn't trace through the slice.

The naive fix (defer cancel() in the loop body) triggers gocritic's deferInLoop. The correct idiomatic Go fix is defer cancel() inside the goroutine's function literal, where:

  • It's not a defer-in-loop (it's inside the anonymous function scope)
  • gosec sees cancel being called
  • Each goroutine cleans up its own context when done
  • LIFO ordering ensures cancel runs before wg.Done

Test plan

  • make lint — 0 issues
  • make test (via pre-commit hook) — all tests pass
  • CI pipeline passes

Move defer cancel() inside goroutines so each goroutine owns its
context lifecycle. This satisfies gosec G118 (cancel not called)
without triggering gocritic deferInLoop.

In iptables/loss.go, also fix a pre-existing closure capture bug:
ctx was reassigned each iteration with = but captured by closure,
so all goroutines shared the last value. Switch to per-iteration
iptCtx with := (matching the netem pattern) to give each goroutine
its own context derived from the original ctx.

Remove stale //nolint:revive on pkg/util/util.go.

Closes #306
Copilot AI review requested due to automatic review settings March 13, 2026 13:08
@github-actions
Copy link

Test Results

546 tests   544 ✅  2s ⏱️
 12 suites    2 💤
  1 files      0 ❌

Results for commit 1eab1f6.

@codecov
Copy link

codecov bot commented Mar 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.45%. Comparing base (8743ab9) to head (78dd41b).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #307      +/-   ##
==========================================
- Coverage   71.91%   71.45%   -0.46%     
==========================================
  Files          46       46              
  Lines        2051     2018      -33     
==========================================
- Hits         1475     1442      -33     
  Misses        576      576              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates several chaos commands to satisfy newer golangci-lint (gosec G118 / nolintlint) checks by ensuring each derived context’s cancel function is invoked in a way linters can trace.

Changes:

  • Remove stale //nolint:revive directive from pkg/util/util.go.
  • Refactor netem/iptables chaos commands to call cancel() inside each goroutine instead of collecting cancel funcs and deferring a cleanup loop.
  • Adjust iptables loss command to avoid reassigning the parent ctx in the loop.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
pkg/util/util.go Removes unused //nolint:revive directive.
pkg/chaos/netem/rate.go Moves context canceling into goroutine for rate injection.
pkg/chaos/netem/loss_state.go Moves context canceling into goroutine for 4-state loss injection.
pkg/chaos/netem/loss_ge.go Moves context canceling into goroutine for GE-model loss injection.
pkg/chaos/netem/loss.go Moves context canceling into goroutine for loss injection.
pkg/chaos/netem/duplicate.go Moves context canceling into goroutine for duplicate injection.
pkg/chaos/netem/delay.go Moves context canceling into goroutine for delay injection.
pkg/chaos/netem/corrupt.go Moves context canceling into goroutine for corruption injection.
pkg/chaos/iptables/loss.go Uses a per-iteration derived context and cancels it in the goroutine.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 109 to +113
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines +122 to +126
iptCtx, cancel := context.WithTimeout(ctx, n.duration)
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
errs[i] = runIPTables(ctx, n.client, c, addCmdPrefix, delCmdPrefix, cmdSuffix, n.srcIPs, n.dstIPs, n.sports, n.dports, n.duration, n.image, n.pull, n.dryRun)
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 113 to +117
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 84 to +88
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 85 to +89
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 89 to +93
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 109 to +113
netemCtx, cancel := context.WithCancel(ctx)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

Comment on lines 101 to +105
netemCtx, cancel := context.WithTimeout(ctx, n.duration)
cancels[i] = cancel
wg.Add(1)
go func(i int, c *container.Container) {
defer wg.Done()
defer cancel()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. All these files use := (short variable declaration), not = (assignment). In Go, := inside a for loop body creates new block-scoped variables per iteration — each goroutine closure captures its own distinct netemCtx/cancel (or iptCtx/cancel). There is no sharing across iterations.

The old iptables/loss.go code did have this bug (it used ctx, cancel = ... with =), which this PR fixes by switching to iptCtx, cancel := ... with :=.

See: https://go.dev/doc/faq#closures_and_goroutines

@alexei-led alexei-led merged commit 9059a92 into master Mar 13, 2026
12 checks passed
@alexei-led alexei-led deleted the fix/golangci-lint-2.11 branch March 13, 2026 17:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pumba 1.0.4 and 1.0.5 does not pass golangci-lint 2.11.1 checks

2 participants